/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.ppamorim.cult.customview;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.util.Timer;
import java.util.TimerTask;

/**
 * 可设置波纹阴影效果的Layout
 *
 * @author xuexiang
 * @since 2019/1/14 下午10:11
 */
public class CultShadowLayout extends BaseShadowLayout
    implements Component.DrawTask, Component.TouchEventListener,
        Component.BindStateChangedListener {
    private static final int TWO = 2;
    private static final int THIRTY = 30;
    private static final int ELASTIC_VALUE = 100;
    private static final int DELAY_TIME = 100;
    private static final int TIME_CONTINUOUS = 1000;
    private static final float ALPHA_UNITE = 255f;
    private static final float FIVE = 5f;
    private static final int RIPPLE_ALPHA = 47;
    private static final int MINUS_ONE = -1;
    private static final HiLogLabel TAG = new HiLogLabel(3, 0xD001100, "ZyTest");
    private int mRoundRadius;
    private Color mRippleColor;
    private int mRippleDuration;
    private int mRippleRadius;
    private float pointX;
    private float pointY;
    private Paint mRipplePaint;
    private RectFloat mRectF;
    private Path mPath;
    private Timer mTimer;
    private TimerTask mTask;
    private int mRippleAlpha;
    private boolean isLongClick = false;
    private MyHandler handler;
    private float oldX;
    private float oldY;
    private float moveX;
    private float moveY;
    private float downX;
    private float downY;

    /**
     * 构造函数
     *
     * @param context 上下文
     */
    public CultShadowLayout(Context context) {
        super(context);
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     * @param attrSet 属性
     */
    public CultShadowLayout(Context context, AttrSet attrSet) {
        super(context, attrSet, "");
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     * @param attrSet 属性
     * @param styleName 样式
     */
    public CultShadowLayout(Context context, AttrSet attrSet, String styleName) {

        super(context, attrSet, styleName);
    }

    @Override
    protected void init(Context context, AttrSet attrSet) {
        super.init(context, attrSet);
//
        mRippleColor = AttrValue.get(attrSet, "sb_ripple_color",
                new Color(Color.BLACK.getValue()));
        mRippleAlpha = AttrValue.get(attrSet, "sb_ripple_alpha", RIPPLE_ALPHA);
        mRippleDuration = AttrValue.get(attrSet, "sb_ripple_duration", TIME_CONTINUOUS);
        mShapeType = AttrValue.get(attrSet, "sb_shape_type", 1);
        mRoundRadius = AttrValue.getDimension(attrSet, "sb_radius", AttrHelper.vp2px(TWO,
                context));

        mRipplePaint = new Paint();
        mRipplePaint.setColor(mRippleColor);
        mRipplePaint.setAlpha(mRippleAlpha / ALPHA_UNITE);
        mRipplePaint.setStyle(Paint.Style.FILL_STYLE);
        mRipplePaint.setAntiAlias(true);

        mPath = new Path();
        mRectF = new RectFloat();
        pointY = MINUS_ONE;
        pointX = MINUS_ONE;
        addDrawTask(this, BETWEEN_BACKGROUND_AND_CONTENT);
        setTouchEventListener(this);
        setBindStateChangedListener(this);
        handler = new MyHandler(EventRunner.getMainEventRunner());
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        super.onDraw(component, canvas);
        if (mRipplePaint == null) {
            return;
        }
        drawFillCircle(canvas);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            MmiPoint mmiPoint = touchEvent.getPointerScreenPosition(0);
            int[] location = component.getLocationOnScreen();
            pointX = mmiPoint.getX() - location[0];
            pointY = mmiPoint.getY() - location[1];
            downX = mmiPoint.getX();
            downY = mmiPoint.getY();
            // 发出延时handler 100
            isLongClick = true;
            InnerEvent event = InnerEvent.get(1);
            handler.sendEvent(event, DELAY_TIME);
        } else if (touchEvent.getAction() == TouchEvent.POINT_MOVE) {
            // 移除handler
            MmiPoint mmiPoint = touchEvent.getPointerScreenPosition(0);
            oldX = oldX > 0 ? oldX : downX;
            oldY = oldY > 0 ? oldY : downY;
            moveX = mmiPoint.getX() - oldX;
            moveY = mmiPoint.getY() - oldY;
            float moveValue = (float) Math.sqrt(moveX * moveX + moveY * moveY);
            if (moveValue > AttrHelper.vp2px(FIVE, getContext())) {
                handler.removeEvent(1);
                pointX = MINUS_ONE;
                pointY = MINUS_ONE;
            }
            oldX = mmiPoint.getX();
            oldY = mmiPoint.getY();
        } else if (touchEvent.getAction() == TouchEvent
            .PRIMARY_POINT_UP) {
            isLongClick = false;
            oldX = MINUS_ONE;
            oldY = MINUS_ONE;
        }
        return true;
    }

    /**
     * Draw ripple effect
     *
     * @param canvas 画布
     */
    private void drawFillCircle(Canvas canvas) {
        if (canvas != null && pointX >= 0 && pointY >= 0) {
            int rbX = getEstimatedWidth();
            int rbY = getEstimatedHeight();
            float xMax = Math.max(pointX, Math.abs(rbX - pointX));
            float yMax = Math.max(pointY, Math.abs(rbY - pointY));
            float longDis = (float) Math.sqrt(xMax * xMax + yMax * yMax);
            if (mRippleRadius > longDis * TWO && !isLongClick) {
                onCompleteDrawRipple();
                return;
            }
            final float drawSpeed = longDis / mRippleDuration * 35;
            mRippleRadius = (int) (mRippleRadius + drawSpeed + ELASTIC_VALUE);

            canvas.save();
            mPath.reset();
            if (mShapeType == 0) {
                mPath.addCircle(rbX >> 1, rbY >> 1, mWidth >> 1, Path.Direction.COUNTER_CLOCK_WISE);
            } else {
                if (hasElevation()) {
                    mRectF = new RectFloat(0 + AttrHelper.vp2px(TWO, getContext()), 0,
                        mWidth - AttrHelper.vp2px(TWO, getContext()),
                        mHeight - AttrHelper.vp2px(FIVE, getContext()));
                } else {
                    mRectF = new RectFloat(0 + AttrHelper.vp2px(TWO, getContext()), 0,
                        mWidth - AttrHelper.vp2px(TWO, getContext()),
                        mHeight);
                }

                if (getCornersLocation() == NO_CORNERS) {
                    mPath.addRoundRect(mRectF, new float[]{0,
                            0, 0, 0, 0, 0, 0, 0}, Path.Direction.COUNTER_CLOCK_WISE);
                } else if (getCornersLocation() == TOP_CORNERS) {
                    mPath.addRoundRect(mRectF, new float[]{THIRTY,
                                    THIRTY, THIRTY, THIRTY, 0, 0, 0, 0},
                        Path.Direction.COUNTER_CLOCK_WISE);
                } else if (getCornersLocation() == BOTTOM_CORNERS) {
                    mPath.addRoundRect(mRectF, new float[]{0, 0, 0, 0,
                                    THIRTY, THIRTY, THIRTY, THIRTY},
                        Path.Direction.COUNTER_CLOCK_WISE);
                }
            }
            canvas.clipPath(mPath, Canvas.ClipOp.INTERSECT);
            canvas.drawCircle(pointX, pointY, mRippleRadius, mRipplePaint);
            canvas.restore();
        }
    }

    /**
     * Start draw ripple effect
     */
    private void onStartDrawRipple() {
        mTask = new TimerTask() {
            @Override
            public void run() {
                new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                    @Override
                    public void run() {
                        invalidate();
                    }
                });
            }
        };
        mTimer = new Timer();
        mTimer.schedule(mTask, 0, THIRTY);
    }

    /**
     * Stop draw ripple effect
     */
    private void onCompleteDrawRipple() {
        if (mTimer != null) {
            if (mTask != null) {
                mTask.cancel();
            }
            mTimer.cancel();
        }
        mRippleRadius = 0;
        pointX = MINUS_ONE;
        pointY = MINUS_ONE;
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        onCompleteDrawRipple();
    }

    public int getRoundRadius() {
        return mRoundRadius;
    }

    /**
     * 设置波纹圆角
     *
     * @param roundRadius 圆角角度
     */
    public void setRoundRadius(int roundRadius) {
        mRoundRadius = roundRadius;
        invalidate();
    }

    public Color getRippleColor() {
        return mRippleColor;
    }

    /**
     * 设置波纹颜色
     *
     * @param rippleColor 颜色值
     */
    public void setRippleColor(Color rippleColor) {
        mRippleColor = rippleColor;
        invalidate();
    }

    public int getRippleDuration() {
        return mRippleDuration;
    }

    /**
     * 设置波纹持续时间
     *
     * @param rippleDuration
     */
    public void setRippleDuration(int rippleDuration) {
        mRippleDuration = rippleDuration;
        invalidate();
    }

    public int getRippleRadius() {
        return mRippleRadius;
    }

    /**
     * 设置波纹圆角
     *
     * @param rippleRadius
     */
    public void setRippleRadius(int rippleRadius) {
        mRippleRadius = rippleRadius;
        invalidate();
    }

    public int getRippleAlpha() {
        return mRippleAlpha;
    }

    /**
     * 设置波纹深浅
     *
     * @param rippleAlpha 深浅Alpha值
     */
    public void setRippleAlpha(int rippleAlpha) {
        mRippleAlpha = rippleAlpha;
        invalidate();
    }

    /**
     * 设置波纹颜色
     *
     * @param maskRes 颜色资源
     */
    public void setRippleRes(int maskRes) {
        setRippleColor(new Color(getContext().getColor(maskRes)));
    }

    /**
     * 设置波纹颜色
     *
     * @param maskRes 颜色值
     */
    public void setRipple(int maskRes) {
        setRippleColor(new Color(maskRes));
    }

    /**
     * handler
     *
     * @since 2021-05-20
     */
    private class MyHandler extends EventHandler {

        /**
         * handler构造函数
         *
         * @param runner EventRunner
         * @throws IllegalArgumentException
         */
        private MyHandler(EventRunner runner) throws IllegalArgumentException {
            super(runner);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            HiLog.info(TAG, "processEvent : " + event.eventId);
            if (event == null) {
                return;
            }
            int eventId = event.eventId;

            switch (eventId) {
                case 1:
                    // down
                    onStartDrawRipple();
                    HiLog.info(TAG, "绘制水波纹");
                    break;
                default:
                    break;
            }
        }
    }
}
